package tomasulogui;

public abstract class FunctionalUnit {

    PipelineSimulator simulator;
    ReservationStation[] stations = new ReservationStation[2];
    int currentCycle = 0;
    boolean requestWriteback = false;
    boolean canWriteback = false;

    public FunctionalUnit(PipelineSimulator sim) {
        simulator = sim;

    }

    public boolean getRequestWriteback() {
        return requestWriteback;
    }

    public void setRequestWriteback(boolean requestWriteback) {
        this.requestWriteback = requestWriteback;
    }

    public boolean getCanWriteback() {
        return canWriteback;
    }

    public void setCanWriteback(boolean canWriteback) {
        this.canWriteback = canWriteback;
    }
    
    public void updateFromCDB(CDB cdb){
        if (cdb.getDataValid() && stations[0] != null) {
            stations[0].snoop(cdb);

            if (stations[1] != null) {
                stations[1].snoop(cdb);
            }
        }

        if(stations[0] != null && stations[1] != null){
            if (stations[1].isReady() && !stations[0].isReady()) {
                ReservationStation temp = stations[0];
                stations[0] = stations[1];
                stations[1] = temp;
            }
        }
    }

    public void squashAll() {
        // todo fill in
        stations[0] = null;
        stations[1] = null;
    }

    public abstract int calculateResult(int station);

    public abstract int getExecCycles();

    public void execCycle(CDB cdb) {
        //todo - start executing, ask for CDB, etc.
        // execute the cycle

        // check if there is valid data
        if (stations[0] != null) {
            if (stations[0].isReady()){
                currentCycle++;
                // check if the instruction is finished
                if (requestWriteback && canWriteback) {
                    // push the data onto the CDB
                    cdb.setDataTag(stations[0].destTag);
                    cdb.setDataValue(calculateResult(0));
                    cdb.setDataValid(true);

                    simulator.getROB().buff[stations[0].destTag].complete = true;
                    simulator.getROB().buff[stations[0].destTag].busy = false;
                    simulator.getROB().buff[stations[0].destTag].setWriteValue(calculateResult(0));
                    simulator.getROB().buff[stations[0].destTag].hasWritten = true;

                    requestWriteback = false;
                    canWriteback = false;

                    // reset the functional unit
                    currentCycle = 0;
                    if (stations[1] != null) {
                        stations[0] = stations[1];
                        stations[1] = null;
                        
                        currentCycle++;
                    } else {
                        stations[0] = null;
                    }

                }
                if (currentCycle == getExecCycles()) {
                    // if the cdb has already been written to then do nothing
                    requestWriteback = true;
                }
            }
        }
    }

    public void acceptIssue(IssuedInst inst) {
        if (stations[0] == null) {
            stations[0] = new ReservationStation(simulator);
            stations[0].loadInst(inst);
            simulator.getROB().buff[stations[0].destTag].busy = true;
            if (simulator.getCDB().getDataValid()) {
                stations[0].snoop(simulator.getCDB());
            }
        } else if (stations[1] == null) {
            stations[1] = new ReservationStation(simulator);
            stations[1].loadInst(inst);
            simulator.getROB().buff[stations[1].destTag].busy = true;
            if (simulator.getCDB().getDataValid()) {
                stations[1].snoop(simulator.getCDB());
            }
        } else {
            System.err.println("ERROR: TRIED TO ADD INSTRUCTION WITH NO RESERVATION STATIONS AVAILABLE");
        }
    }
}
